/*
    mc146818 driver
    Copyright (C) 2011  Jiabo <jiabo2011@gmail.com>

    All functions copied from qemu 0.13 mc146818rtc.c

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "../ioport.h"
#include "mc146818.h"

#define DEBUG_CMOS

#ifdef DEBUG_CMOS
#define DPRINTF(fmt, ...)                                       \
    do { printf("CMOS: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...)
#endif

#define RTC_SECONDS             0
#define RTC_SECONDS_ALARM       1
#define RTC_MINUTES             2
#define RTC_MINUTES_ALARM       3
#define RTC_HOURS               4
#define RTC_HOURS_ALARM         5
#define RTC_ALARM_DONT_CARE    0xC0

#define RTC_DAY_OF_WEEK         6
#define RTC_DAY_OF_MONTH        7
#define RTC_MONTH               8
#define RTC_YEAR                9

#define RTC_REG_A               10
#define RTC_REG_B               11
#define RTC_REG_C               12
#define RTC_REG_D               13
/* PC cmos mappings */
#define REG_EQUIPMENT_BYTE          0x14

typedef struct RTCState {
	uint8_t cmos_data[128];
	uint8_t cmos_index;
} RTCState;

static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
{
	RTCState *s = opaque;
	
	if ((addr & 1) == 0) {
		s->cmos_index = data & 0x7f;
	} else {
		DPRINTF("write index=0x%02x val=0x%02x\n",
			s->cmos_index, data);
		switch(s->cmos_index) {
		case RTC_SECONDS_ALARM:
		case RTC_MINUTES_ALARM:
		case RTC_HOURS_ALARM:
			s->cmos_data[s->cmos_index] = data;
			break;
		case RTC_SECONDS:
		case RTC_MINUTES:
		case RTC_HOURS:
		case RTC_DAY_OF_WEEK:
		case RTC_DAY_OF_MONTH:
		case RTC_MONTH:
		case RTC_YEAR:
			s->cmos_data[s->cmos_index] = data;
			break;
		case RTC_REG_A:
			break;
		case RTC_REG_B:
			s->cmos_data[RTC_REG_B] = data;
			break;
		case RTC_REG_C:
		case RTC_REG_D:
			/* cannot write to them */
			break;
		default:
			s->cmos_data[s->cmos_index] = data;
			break;
		}
	}
}

static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
{
	RTCState *s = opaque;
	int ret;
	if ((addr & 1) == 0) {
		return 0xff;
	} else {
		switch(s->cmos_index) {
		case RTC_SECONDS:
		case RTC_MINUTES:
		case RTC_HOURS:
		case RTC_DAY_OF_WEEK:
		case RTC_DAY_OF_MONTH:
		case RTC_MONTH:
		case RTC_YEAR:
			ret = s->cmos_data[s->cmos_index];
			break;
		case RTC_REG_A:
			ret = s->cmos_data[s->cmos_index];
			break;
		case RTC_REG_C:
			ret = s->cmos_data[s->cmos_index];
			s->cmos_data[RTC_REG_C] = 0x00;
			break;
		default:
			ret = s->cmos_data[s->cmos_index];
			break;
		}
		DPRINTF("read index=0x%02x val=0x%02x\n",
			s->cmos_index, ret);
		return ret;
	}
}

void mc146818_init(void)
{
	RTCState *s;
	s = calloc(1, sizeof(RTCState));

	/* various important CMOS locations needed by PC/Bochs bios */
	s->cmos_data[REG_EQUIPMENT_BYTE] = 0x02; /* FPU is there */
	s->cmos_data[REG_EQUIPMENT_BYTE] |= 0x04; /* PS/2 mouse installed */

//	s->cmos_data[0x3d] = 0x02; /* hard drive boot */
	s->cmos_data[0x3d] = 0x03; /* CD-ROM boot */

	register_ioport_write(0x70, 2, 1, cmos_ioport_write, s);
	register_ioport_read(0x70, 2, 1, cmos_ioport_read, s);
}

